#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>  // 用于创建目录
#include <stdarg.h>    // 用于可变参数
#include <errno.h>     // 用于错误处理
#include <dirent.h>    // 用于目录操作
#include <unistd.h>    // 用于 unlink
#include "log.h"
#include "config.h"

#define LOG_DIR          "./log"
#define THRESHOLD_SIZE   (500LL * 1024 * 1024)  // 500MB
#define TARGET_REMAINING (THRESHOLD_SIZE / 3)   // 目标剩余大小

// 删除 LOG_DIR 下所有内容（包括子目录和文件），但保留 LOG_DIR 本身
void delete_all_in_log(const char *dirpath)
{
    DIR *dir = opendir(dirpath);
    if (!dir)
        return;
    struct dirent *entry;
    char path[512];
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        snprintf(path, sizeof(path), "%s/%s", dirpath, entry->d_name);
        struct stat st;
        if (stat(path, &st) == 0) {
            if (S_ISDIR(st.st_mode)) {
                delete_all_in_log(path);
                rmdir(path);
            }
            else {
                unlink(path);
            }
        }
    }
    closedir(dir);
}

// 递归删除指定目录下所有内容，并删除该目录本身
void delete_dir_recursive(const char *path)
{
    delete_all_in_log(path);
    rmdir(path);
}

// 递归计算指定目录下所有文件的大小
long get_total_size(const char *dirpath)
{
    DIR *dir = opendir(dirpath);
    if (!dir)
        return 0;
    struct dirent *entry;
    char path[512];
    struct stat st;
    long total = 0;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        snprintf(path, sizeof(path), "%s/%s", dirpath, entry->d_name);
        if (stat(path, &st) == 0) {
            if (S_ISDIR(st.st_mode))
                total += get_total_size(path);
            else
                total += st.st_size;
        }
    }
    closedir(dir);
    return total;
}

// 辅助结构体，用于保存符合日期格式目录的信息
typedef struct {
    char name[256];  // 如 "2025-3-20"
    int date_val;    // 用于比较的整数形式：YYYYMMDD，例如 2025-03-20 -> 20250320
    char path[512];
    long size;  // 该目录下所有文件的大小
} log_dir_entry;

// 判断目录名是否符合日期格式（例如 "2025-3-20"），并解析出年、月、日
int is_date_format(const char *dirname, int *year, int *month, int *day)
{
    if (sscanf(dirname, "%d-%d-%d", year, month, day) == 3)
        return 1;
    return 0;
}

// 检查 log 目录，根据当前总大小和目标剩余容量删除旧目录
void check_and_cleanup_logs()
{
    long total_size = get_total_size(LOG_DIR);
    if (total_size < THRESHOLD_SIZE)
        return;

    printf("Total log size = %ld bytes exceeds threshold (%ld bytes).\n", total_size, THRESHOLD_SIZE);

    time_t now = time(NULL);
    struct tm tm_now;
    localtime_r(&now, &tm_now);
    int today_val = (tm_now.tm_year + 1900) * 10000 + (tm_now.tm_mon + 1) * 100 + tm_now.tm_mday;

    DIR *dir = opendir(LOG_DIR);
    if (!dir) {
        perror("opendir failed");
        return;
    }

    int capacity = 64, count = 0;
    log_dir_entry *entries = malloc(capacity * sizeof(log_dir_entry));
    if (!entries) {
        perror("malloc failed");
        closedir(dir);
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        int year, month, day;
        if (!is_date_format(entry->d_name, &year, &month, &day))
            continue;

        int date_val = year * 10000 + month * 100 + day;
        if (date_val >= today_val)
            continue;

        char full_path[512];
        snprintf(full_path, sizeof(full_path), "%s/%s", LOG_DIR, entry->d_name);

        struct stat st;
        if (stat(full_path, &st) != 0 || !S_ISDIR(st.st_mode))
            continue;

        if (count >= capacity) {
            capacity *= 2;
            log_dir_entry *tmp = realloc(entries, capacity * sizeof(log_dir_entry));
            if (!tmp) {
                perror("realloc failed");
                free(entries);
                closedir(dir);
                return;
            }
            entries = tmp;
        }

        strncpy(entries[count].name, entry->d_name, sizeof(entries[count].name));
        strncpy(entries[count].path, full_path, sizeof(entries[count].path));
        entries[count].date_val = date_val;
        entries[count].size = get_total_size(full_path);
        count++;
    }
    closedir(dir);

    if (count == 0) {
        printf("No dated subdirectories found; deleting all content in log directory.\n");
        delete_all_in_log(LOG_DIR);
        free(entries);
        return;
    }

    for (int i = 0; i < count - 1; i++) {
        for (int j = i + 1; j < count; j++) {
            if (entries[i].date_val > entries[j].date_val) {
                log_dir_entry tmp = entries[i];
                entries[i] = entries[j];
                entries[j] = tmp;
            }
        }
    }

    long remaining = total_size;
    int cleanup_done = 0;
    for (int i = 0; i < count; i++) {
        if (remaining <= TARGET_REMAINING) {
            cleanup_done = 1;
            break;
        }
        printf("Deleting directory: %s (size: %ld bytes)\n", entries[i].path, entries[i].size);
        delete_dir_recursive(entries[i].path);
        remaining -= entries[i].size;
    }

    if (!cleanup_done && remaining > TARGET_REMAINING) {
        printf("After deleting dated directories, remaining size = %ld bytes > target (%ld bytes).\nDeleting all content in log directory.\n", remaining, TARGET_REMAINING);
        delete_all_in_log(LOG_DIR);
    }
    else {
        printf("Cleanup done. Remaining log size is %ld bytes.\n", remaining);
    }

    free(entries);
}

// 递归计算目录下所有文件的总大小
unsigned long long get_directory_size(const char *path)
{
    unsigned long long total = 0;
    struct stat st;

    if (stat(path, &st) == -1) {
        perror("stat");
        return 0;
    }

    // 如果是常规文件，则返回文件大小
    if (S_ISREG(st.st_mode)) {
        return st.st_size;
    }

    // 如果是目录，则递归遍历子项
    if (S_ISDIR(st.st_mode)) {
        DIR *dir = opendir(path);
        if (!dir) {
            perror("opendir");
            return 0;
        }

        struct dirent *entry;
        while ((entry = readdir(dir)) != NULL) {
            // 跳过 . 和 ..
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;

            char fullpath[1024];
            snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
            total += get_directory_size(fullpath);
        }
        closedir(dir);
    }

    return total;
}

// 递归删除目录及其内容
int delete_directory(const char *path)
{
    DIR *dir = opendir(path);
    if (!dir) {
        perror("opendir in delete_directory");
        return -1;
    }

    struct dirent *entry;
    int ret = 0;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        char fullpath[1024];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);

        struct stat st;
        if (stat(fullpath, &st) == -1) {
            perror("stat in delete_directory");
            ret = -1;
            continue;
        }

        if (S_ISDIR(st.st_mode)) {
            if (delete_directory(fullpath) != 0) {
                ret = -1;
            }
        }
        else {
            if (unlink(fullpath) != 0) {
                perror("unlink");
                ret = -1;
            }
        }
    }
    closedir(dir);
    // 删除目录本身
    if (rmdir(path) != 0) {
        perror("rmdir");
        ret = -1;
    }
    return ret;
}

// 用于 qsort 比较目录名称（假设名称格式可直接比较日期先后）
int compare_dirs(const void *a, const void *b)
{
    const char *dir1 = *(const char **)a;
    const char *dir2 = *(const char **)b;
    return strcmp(dir1, dir2);
}

// 检测指定目录下所有文件总大小，若超过阈值则按日期目录排序，删除一半最旧的目录
void check_and_cleanup(const char *base_path, unsigned long long threshold)
{
    unsigned long long total_size = get_directory_size(base_path);
    printf("目录 %s 下所有文件的总大小: %llu 字节\n", base_path, total_size);

    if (total_size <= threshold) {
        printf("总大小未超过阈值 (%llu 字节)，无需删除。\n", threshold);
        return;
    }

    printf("总大小超过阈值 (%llu 字节)，开始清理...\n", threshold);

    // 列出 base_path 下的所有子目录（假定这些为日期目录）
    DIR *dir = opendir(base_path);
    if (!dir) {
        perror("opendir in check_and_cleanup");
        return;
    }

    char **dir_list = NULL;
    int count = 0;
    struct dirent *entry;

    while ((entry = readdir(dir)) != NULL) {
        // 判断是否为目录
        // 注意：d_type 可能为 DT_UNKNOWN，此时需要调用 stat 判断
        if (entry->d_type != DT_DIR) {
            continue;
        }
        // 跳过 "." 和 ".."
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        // 构造完整目录路径
        char *subdir = malloc(1024);
        if (!subdir) {
            perror("malloc");
            continue;
        }
        snprintf(subdir, 1024, "%s/%s", base_path, entry->d_name);

        // 可以在这里添加额外判断，比如检查目录名称是否符合日期格式

        // 添加到目录列表中
        dir_list = realloc(dir_list, sizeof(char *) * (count + 1));
        if (!dir_list) {
            perror("realloc");
            free(subdir);
            break;
        }
        dir_list[count++] = subdir;
    }
    closedir(dir);

    if (count == 0) {
        printf("没有找到可供删除的子目录。\n");
        return;
    }

    // 按目录名称排序，假设名称为日期格式（例如 YYYY-MM-DD 或 YYYYMMDD），可直接比较日期先后
    qsort(dir_list, count, sizeof(char *), compare_dirs);

    // 删除排序后的最旧一半目录（即数组前半部分）
    int to_delete = count / 2;
    printf("共发现 %d 个日期目录，将删除其中最旧的 %d 个目录。\n", count, to_delete);

    for (int i = 0; i < to_delete; i++) {
        printf("删除目录: %s\n", dir_list[i]);
        if (delete_directory(dir_list[i]) != 0) {
            fprintf(stderr, "删除目录 %s 失败\n", dir_list[i]);
        }
        free(dir_list[i]);
    }
    // 剩下的目录释放内存
    for (int i = to_delete; i < count; i++) {
        free(dir_list[i]);
    }
    free(dir_list);
}

// 递归创建目录
void create_directory_if_not_exists(const char *path)
{
    // 复制路径字符串
    char *temp = strdup(path);
    if (temp == NULL) {
        perror("strdup 失败");
        return;
    }

    // 如果路径以 '/' 开头，保留根目录，然后从下一个字符开始处理
    char *start = temp;
    if (temp[0] == '/') {
        start = temp + 1;
    }

    // 指针 pos 用于遍历路径，从 start 开始
    char *pos = start;
    while (1) {
        // 找到下一个目录分隔符
        pos = strchr(pos, '/');
        if (pos != NULL) {
            *pos = '\0';  // 暂时截断路径
        }

        // 如果 temp 是空字符串（绝对路径的根目录），则直接跳过创建
        if (strlen(temp) > 0) {
            struct stat st = {0};
            if (stat(temp, &st) == -1) {
                if (mkdir(temp, 0755) != 0 && errno != EEXIST) {
                    perror("创建目录失败");
                    free(temp);
                    return;
                }
            }
        }

        if (pos == NULL) {
            break;
        }

        // 恢复路径分隔符，并移动 pos 指针继续处理后续目录
        *pos = '/';
        pos++;
    }

    free(temp);
}

// 获取当前时间，格式化为 <年-月-日 时:分:秒:毫秒>
char *get_current_time_string(char *time_str, size_t size)
{
    static char time_buffer[64];
    if (time_str == NULL) {
        time_str = time_buffer;
        size = sizeof(time_buffer);
    }
    struct timeval tv;
    gettimeofday(&tv, NULL);  // 获取当前时间（精确到微秒）

    struct tm *tm_info = localtime(&tv.tv_sec);               // 转换为本地时间
    strftime(time_str, size, "<%Y-%m-%d %H:%M:%S", tm_info);  // 格式化时间

    // 添加毫秒部分
    char ms_str[24];
    snprintf(ms_str, sizeof(ms_str), ":%03ld>", tv.tv_usec / 1000);  // 微秒转毫秒
    strncat(time_str, ms_str, size - strlen(time_str) - 1);
    return time_str;  // 返回格式化后的时间字符串
}

void print_current_time(void)
{
    char time_str[64];
    get_current_time_string(time_str, sizeof(time_str));
    printf("%s\n", time_str);
}

// 写入日志内容（可变参数版本）
void write_log(const char *filename, const char *format, ...)
{
    if (g_config.log_switch == 0) {
        return;
    }
    // 清理日志（例如删除旧日志等）
    check_and_cleanup_logs();

    // 获取当前时间字符串
    char time_str[64];
    get_current_time_string(time_str, sizeof(time_str));

    // 生成日志目录，格式为 "log/年-月-日"
    char log_dir[256];
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(log_dir, sizeof(log_dir), "log/%Y-%m-%d", tm_info);

    // 创建日志目录（如果不存在）
    create_directory_if_not_exists(log_dir);

    // 生成日志文件完整路径：log/年-月-日/文件名.log
    char log_filepath[512];
    snprintf(log_filepath, sizeof(log_filepath), "%s/%s.log", log_dir, filename);

    // 静态记录本次程序运行中已写过的日志文件路径
    // 这里最多保存 100 个文件路径，实际项目中可根据需要扩展
    static char *opened_log_files[100] = {0};
    static int opened_log_count = 0;
    int file_already_opened = 0;
    for (int i = 0; i < opened_log_count; i++) {
        if (strcmp(opened_log_files[i], log_filepath) == 0) {
            file_already_opened = 1;
            break;
        }
    }

    // 根据是否已写过，选择打开模式：未写过用 "w"，已写过用 "a"
    FILE *log_file = fopen(log_filepath, file_already_opened ? "a" : "w");
    if (log_file == NULL) {
        perror("Failed to open log file");
        return;
    }
    // 如果该日志文件还未被记录，则将其加入记录列表
    if (!file_already_opened) {
        opened_log_files[opened_log_count] = strdup(log_filepath);
        if (opened_log_files[opened_log_count] != NULL)
            opened_log_count++;
    }

    // 写入时间戳和第一行内容
    fprintf(log_file, "%s ", time_str);

    va_list args;
    va_start(args, format);
    vfprintf(log_file, format, args);
    va_end(args);
    // 写入换行符
    fprintf(log_file, "\n");

    // 处理后续可变参数（多行日志），直到遇到 NULL
    va_start(args, format);
    const char *line;
    while ((line = va_arg(args, const char *)) != NULL) {
        fprintf(log_file, "%s\n", line);
    }
    va_end(args);
    fclose(log_file);
}

void print_hex_buffer(const char *buf, int len)
{
    for (int i = 0; i < len; i++) {
        printf("%02X ", (unsigned char)buf[i]);
        if ((i + 1) % 32 == 0) {
            printf("\n");
        }
    }
    printf("\n");
}

char *get_date(char *date)
{
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(date, 11, "%Y-%m-%d", tm_info);
    return date;
}

void write_hex_log(const char *filename, const char *title, const char *buf, int len)
{
    if (g_config.log_switch == 0) {
        return;
    }
    // 清理日志（例如删除旧日志等）
    check_and_cleanup_logs();
    // 获取当前时间
    char time_str[64];
    get_current_time_string(time_str, sizeof(time_str));

    // 生成日志目录：log/年-月-日
    char log_dir[256];
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(log_dir, sizeof(log_dir), "./log/%Y-%m-%d", tm_info);

    // 创建日志目录（如果不存在）
    create_directory_if_not_exists(log_dir);

    // 生成日志文件路径：log/年-月-日/文件名.log
    char log_filepath[512];
    snprintf(log_filepath, sizeof(log_filepath), "%s/%s.log", log_dir, filename);

    // 打开日志文件（追加模式）
    FILE *log_file = fopen(log_filepath, "a");
    if (log_file == NULL) {
        perror("Failed to open log file");
        printf("file path: %s\n", log_filepath);
        return;
    }

    // 写入时间戳和标题
    fprintf(log_file, "%s %s\n", time_str, title);

    // 打印十六进制内容
    for (int i = 0; i < len; i++) {
        fprintf(log_file, "%02X ", (unsigned char)buf[i]);
    }
    fprintf(log_file, "\n");

    // 关闭文件
    fclose(log_file);
}

// 函数返回 0 表示成功，非 0 表示出错
int copy_file_with_overwrite(const char *srcFile, const char *destFile)
{
    // 如果目标文件存在，先尝试删除
    if (remove(destFile) != 0) {
        // 如果删除失败且错误不是“文件不存在”，则打印错误信息
        if (errno != ENOENT) {
            fprintf(stderr, "删除目标文件 %s 失败: %s\n", destFile, strerror(errno));
            return -1;
        }
    }

    // 打开源文件，采用二进制读模式
    FILE *in = fopen(srcFile, "rb");
    if (in == NULL) {
        fprintf(stderr, "打开源文件 %s 失败: %s\n", srcFile, strerror(errno));
        return -1;
    }

    // 打开目标文件，采用二进制写模式
    FILE *out = fopen(destFile, "wb");
    if (out == NULL) {
        fprintf(stderr, "打开目标文件 %s 失败: %s\n", destFile, strerror(errno));
        fclose(in);
        return -1;
    }

    // 使用缓冲区进行复制
    char buffer[4096];
    size_t bytesRead;
    while ((bytesRead = fread(buffer, 1, sizeof(buffer), in)) > 0) {
        size_t bytesWritten = fwrite(buffer, 1, bytesRead, out);
        if (bytesWritten != bytesRead) {
            fprintf(stderr, "写入目标文件 %s 时出错\n", destFile);
            fclose(in);
            fclose(out);
            return -1;
        }
    }

    // 关闭文件
    fclose(in);
    fclose(out);
    return 0;
}